package cn.coder.easyjson;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import cn.coder.easyjson.stream.JSONReader;
import cn.coder.easyjson.util.ObjectUtils;

public class JSONArray {

	private static final String EMPTY_ARRAY = "[]";
	private static final int DEFAULT_CAPACITY = 10;
	private final List<Object> innerList;
	private JSONReader reader;

	public JSONArray() {
		this(DEFAULT_CAPACITY);
	}

	public JSONArray(int len) {
		this(null, null, len);
	}

	public JSONArray(JSONReader reader) {
		this(reader, null, DEFAULT_CAPACITY);
	}

	public JSONArray(Collection<?> obj) {
		this(null, obj, DEFAULT_CAPACITY);
	}

	private JSONArray(JSONReader r, Collection<?> c, int capacity) {
		if (c != null) {
			this.innerList = new ArrayList<>(c.size());
			this.innerList.addAll(c);
		} else
			this.innerList = new ArrayList<>(capacity);
		this.reader = r;
	}

	public void add(Object item) {
		this.innerList.add(item);
	}

	public void set(int idx, Object item) {
		this.innerList.set(idx, item);
	}

	public int size() {
		return this.innerList.size();
	}

	public Object get(int idx) {
		Object temp = this.innerList.get(idx);
		if (this.reader != null) {
			temp = this.reader.lazy(temp);
			this.set(idx, temp);
		}
		return temp;
	}

	public JSONObject getJSONObject(int index) {
		Object obj = get(index);
		if (obj instanceof JSONObject)
			return (JSONObject) obj;
		throw new RuntimeException("this index " + index + " not a json object");
	}

	public String toJson() {
		final int size = this.size();
		if (size == 0)
			return EMPTY_ARRAY;
		StringBuffer temp = new StringBuffer("[");
		for (int i = 0; i < size; i++) {
			ObjectUtils.appendValue(temp, get(i));
			temp.append(",");
		}
		if (temp.length() > 1)
			temp.delete(temp.length() - 1, temp.length());
		temp.append("]");
		return temp.toString();
	}

	@SuppressWarnings("unchecked")
	public <T> List<T> parse(Class<T> clazz) {
		final int size = this.size();
		if (size == 0)
			return Collections.EMPTY_LIST;
		List<T> temp = new ArrayList<>();
		for (int i = 0; i < size; i++) {
			if (isNumber(clazz) || isBoolean(clazz) || isString(clazz)) {
				temp.add((T) ObjectUtils.cast(clazz, get(i)));
			} else {
				temp.add(getJSONObject(i).parse(clazz));
			}
		}
		return temp;
	}

	private static boolean isString(Class<?> clazz) {
		return CharSequence.class.isAssignableFrom(clazz);
	}

	private static boolean isNumber(Class<?> clazz) {
		return Number.class.isAssignableFrom(clazz);
	}

	private static boolean isBoolean(Class<?> clazz) {
		return Boolean.class.isAssignableFrom(clazz);
	}

}
